package model.algorithms.rounding;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;

import model.components.networks.network.VirtualNetwork;
import model.components.networks.network.PhysicalNetwork;
import model.components.nodes.node.VirtualNode;
import model.components.nodes.node.PhysicalNode;

public class Randomized extends RoundingAlgorithm
{
	public Randomized( String name )
	{
		super( name );
	}
	
	@Override
	public LinkedHashMap< VirtualNode, PhysicalNode > run( VirtualNetwork virtualNetwork, PhysicalNetwork physicalNetwork, double x[][][], double f[][][] )
	{
		LinkedHashMap< VirtualNode, PhysicalNode > mappingNodes = new LinkedHashMap< VirtualNode, PhysicalNode >();
		
		PhysicalNetwork substrateCopy = physicalNetwork.getCopy();
		
		LinkedHashMap< PhysicalNode, Integer > correspondence = new LinkedHashMap< PhysicalNode, Integer >();

		for( VirtualNode nodeRequest: virtualNetwork.getVirtualNodes() )
			if( substrateCopy.getNodes().size() != 0 )
			{
				int iNodeReq = nodeRequest.getNumber();
				double sumFlowsTotal = 0;
				LinkedHashMap< PhysicalNode, Double > nodesSubSumFlows = new LinkedHashMap< PhysicalNode, Double >(); 
		     
				for( PhysicalNode nodeSubstrate: substrateCopy.getPhysicalNodes() )
					if( nodeRequest.getType().equalsIgnoreCase( nodeSubstrate.getType() ) )
					{
						int iNodeAug =  nodeSubstrate.getNumber() + virtualNetwork.getNodes().size();
						
						double sumFlows = 0;
		      
						for( int i = 0; i < virtualNetwork.getVirtualLinks().size(); i++ )
							sumFlows += x[ i ][ iNodeReq ][ iNodeAug ] * ( f[ i ][ iNodeReq ][ iNodeAug ] + f[ i ][ iNodeAug ][ iNodeReq ] );
	     
						sumFlowsTotal += sumFlows;
						nodesSubSumFlows.put( nodeSubstrate, sumFlows );
					}
				
				for( PhysicalNode nodeSubstrate: substrateCopy.getPhysicalNodes() )
					if( nodesSubSumFlows.containsKey( nodeSubstrate ) )
					{
						double averageSumFlows = nodesSubSumFlows.get( nodeSubstrate ) / sumFlowsTotal;
						nodesSubSumFlows.put( nodeSubstrate, averageSumFlows );
					}
				
				Set< PhysicalNode > nodesSub = nodesSubSumFlows.keySet();
				sumFlowsTotal = 0;
				ArrayList< Double > sumsFlowsTotal = new ArrayList< Double >();
				
				for( Iterator< PhysicalNode > iterator = nodesSub.iterator(); iterator.hasNext(); )
				{
					PhysicalNode nodeSubstrate = iterator.next();
					sumFlowsTotal += nodesSubSumFlows.get( nodeSubstrate );
					sumsFlowsTotal.add( sumFlowsTotal );
					correspondence.put( nodeSubstrate, sumsFlowsTotal.size() - 1 );
				}
				
				double random = Math.random() * sumFlowsTotal;
				int iNodeSubSel = -1;
				
				for( int i = 0; i < sumsFlowsTotal.size(); i++ )
					if( sumsFlowsTotal.get( i ) > random  )
					{
						iNodeSubSel = i;
						break;
					}
				
				PhysicalNode nodeSubSel = null;
				
				for( Iterator< PhysicalNode > iterator = nodesSub.iterator(); iterator.hasNext(); )
				{
					PhysicalNode nodeSubstrate = iterator.next();
					
					if( Integer.valueOf( iNodeSubSel ).compareTo( correspondence.get( nodeSubstrate ) ) == 0 )
					{
						nodeSubSel = nodeSubstrate;
						break;
					}
				}
				
				mappingNodes.put( nodeRequest, nodeSubSel );
				substrateCopy.getNodes().remove( nodeSubSel );
		    }
		    else	mappingNodes = null;
		
		return mappingNodes;
	}
	
}
